package bma.common.langutil.jmshell.jvm;

import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;

import bma.common.langutil.jmshell.Session;
import bma.common.langutil.jmshell.cmdopt.CommandOptionParser;
import bma.common.langutil.jmshell.command.AbstractSimpleCommandProcessor;
import bma.common.langutil.jmshell.common.JMSPager;

public class JVMThreadCommand extends AbstractSimpleCommandProcessor {

	private static final String PAGER_KEY = "@jvm_thread";

	public JVMThreadCommand() {
		super("thread");
	}

	@Override
	public CommandOptionParser createCommandOption(Session session) {
		CommandOptionParser r = new CommandOptionParser();
		r.addIntegerOption('s', "size", "show thread info page size");
		r.addIntegerOption('p', "pos",
				"show thread info from which position, default last position");
		r.addBooleanOption('d', "detail", "show thread detail");
		r.addIntegerOption('m', "maxstack", "max thread stack, default 10");
		r.addBooleanOption('r', "reset", "clear thread info cache");
		r.addLongOption('t', "threadid", "show which thread info");
		r.addBooleanOption('w', "wait", "show waiting/blocking thread only");
		r.addStringOption('f', "filter", "thread name filter");
		r.addBooleanOption('c', "create", "create thread info");
		return r;
	}

	@Override
	public boolean processCommand(Session s, String cmd,
			CommandOptionParser options) {
		boolean reset = options.getOptionValue("reset", Boolean.FALSE);
		if (reset) {
			resetPager(s);
			return true;
		}

		boolean create = options.getOptionValue("create", Boolean.FALSE);
		if (create) {
			boolean detail = options.getOptionValue("detail", Boolean.FALSE);
			int maxs = options.getOptionValue("maxstack", new Integer(10));
			long tid = options.getOptionValue("threadid", new Long(0));
			boolean waitOnly = options.getOptionValue("wait", Boolean.FALSE);
			Collection<String> filters = options.getOptionValues("filter",
					String.class);
			createPager(s, tid, detail, waitOnly, filters, maxs);
			return true;
		}
		int sz = options.getOptionValue("size", new Integer(-1));
		if (sz > 0) {
			JMSPager pager = JMSPager.getData(s, PAGER_KEY, true);
			pager.setSize(sz);
		}
		threadInfo(s, options);
		return true;
	}

	public void threadInfo(Session s, CommandOptionParser options) {

		ThreadMXBean tb = ManagementFactory.getThreadMXBean();
		int threadCount = tb.getThreadCount();

		JMSPager pager = JMSPager.getData(s, PAGER_KEY, false);
		int total = pager == null ? 0 : pager.total();

		s.writeln("[thread " + total + "/" + threadCount + "]");
		if (pager != null) {
			int pos = options.getOptionValue("pos", new Integer(-1));
			pager.show(s, pos, -1, new JMSPager.Render<ThreadInfo>() {
				@Override
				public void show(Session s, ThreadInfo obj, int pos) {
					s.write("[T] " + obj.toString());
				}
			}, ThreadInfo.class);
		} else {
			s.writeln("use 'thread -c' to create thread info");
		}
		s.writeln("[thread end]");
	}

	protected void createPager(Session s, long tid, boolean detail,
			boolean waitOnly, Collection<String> filters, int maxs) {

		ThreadMXBean tb = ManagementFactory.getThreadMXBean();

		List<ThreadInfo> threadInfoList = new LinkedList<ThreadInfo>();
		if (tid > 0) {
			ThreadInfo info = tb.getThreadInfo(tid, detail ? maxs : 0);
			threadInfoList.add(info);
		} else {
			long tids[] = tb.getAllThreadIds();
			for (long l : tids) {
				ThreadInfo info = tb.getThreadInfo(l, detail ? maxs : 0);
				if (info != null) {
					if (waitOnly) {
						Thread.State state = info.getThreadState();
						switch (state) {
						case BLOCKED:
						case TIMED_WAITING:
						case WAITING:
							break;
						default:
							continue;
						}
					}
					if (filters != null && filters.size() > 0) {
						boolean fmatch = false;
						String name = info.getThreadName();
						for (String fi : filters) {
							if (name.indexOf(fi) != -1) {
								fmatch = true;
								break;
							}
						}
						if (!fmatch)
							continue;
					}
					threadInfoList.add(info);
				}
			}
		}

		JMSPager pg = JMSPager.getData(s, PAGER_KEY, true);
		pg.init(threadInfoList);

		s.writeln("thread info created, use 'thread' to view");
	}

	protected void resetPager(Session s) {
		JMSPager pg = JMSPager.getData(s, PAGER_KEY, false);
		if (pg != null) {
			pg.reset();
		}
		s.writeln("thread info reset");
	}
}